Qintong BaBa

一天进步一点


  • 首页

  • 分类

  • 归档

  • 标签

  • 关于

  • 搜索

JavaWeb学习总结(十三)--使用session防止表单重复提交

发表于 2017-06-30 |

JavaWeb学习总结(十二)--Session

发表于 2017-06-30 | 分类于 Java Web |

[TOC]

一、Session简单介绍

  在WEB开发中,服务器可以为每个用户浏览器创建一个会话对象(session对象),注意:一个浏览器独占一个session对象(默认情况下)。因此,在需要保存用户数据时,服务器程序可以把用户数据写到用户浏览器独占的session中,当用户使用浏览器访问其它程序时,其它程序可以从用户的session中取出该用户的数据,为用户服务。

二、Session和Cookie的主要区别

  • Cookie是把用户的数据写给用户的浏览器。
  • Session技术把用户的数据写到用户独占的session中。
  • Session对象由服务器创建,开发人员可以调用request对象的getSession方法得到session对象。

三、session实现原理

3.1、服务器是如何实现一个session为一个用户浏览器服务的?

  服务器创建session出来后,会把session的id号,以cookie的形式回写给客户机,这样,只要客户机的浏览器不关,再去访问服务器时,都会带着session的id号去,服务器发现客户机浏览器带session id过来了,就会使用内存中与之对应的session为之服务。可以用如下的代码证明:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
package org.wuqinghua.java.ch03;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
/**
* Created by wuqinghua on 17/6/30.
*/
@WebServlet(urlPatterns = "/sessionDemo01")
public class SessionDemo01 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setCharacterEncoding("UTF-8");
resp.setContentType("text/html;charset=UTF-8");
HttpSession session = req.getSession();//获取session
//将数据存储进去
session.setAttribute("data","孤傲苍狼");
//获取session的id
String sessionId = session.getId();
//判断session是否为新的
if(session.isNew()){
resp.getWriter().print("session创建成功!sessionId为:"+sessionId);
}else{
resp.getWriter().print("服务器已经存在该session了,sessionId为:"+sessionId);
}
}
}

第一次访问时,服务器会创建一个新的sesion,并且把session的Id以cookie的形式发送给客户端浏览器,如下图所示:  img

  点击刷新按钮,再次请求服务器,此时就可以看到浏览器再请求服务器时,会把存储到cookie中的session的Id一起传递到服务器端了,如下图所示:

img

  我猜想request.getSession()方法内部新创建了Session之后一定是做了如下的处理

1
2
3
4
5
6
7
//获取session的Id
String sessionId = session.getId();
//将session的Id存储到名字为JSESSIONID的cookie中
Cookie cookie = new Cookie("JSESSIONID", sessionId);
//设置cookie的有效路径
cookie.setPath(request.getContextPath());
response.addCookie(cookie);

四、浏览器禁用Cookie后的session处理

4.1、IE8禁用cookie

1
  工具->internet选项->隐私->设置->将滑轴拉到最顶上(阻止所有cookies)

  imgimg

4.2、解决方案:URL重写

  response.encodeRedirectURL(java.lang.String url) 用于对sendRedirect方法后的url地址进行重写。
  response.encodeURL(java.lang.String url)用于对表单action和超链接的url地址进行重写

4.3、范例:禁用Cookie后servlet共享Session中的数据

IndexServlet

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
package org.wuqinghua.java.ch03;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Map;
/**
* Created by wuqinghua on 17/6/30.
*/
@WebServlet(urlPatterns = "/index")
public class IndexServlet extends HttpServlet{
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setCharacterEncoding("UTF-8");
resp.setContentType("text/html;charset=UTF-8");
PrintWriter out = resp.getWriter();
//创建session
req.getSession();
out.print("本网站有如下图书:<br />");
Map<String,Book> books = DB.getAll();
for (Map.Entry<String,Book> book: books.entrySet()) {
Book b = book.getValue();
String url = req.getContextPath()+"/buyServlet?id="+b.getId();
url = resp.encodeURL(url);//对于表单的action和超链接使用此方法重写
out.print(b.getName()+"<a href='"+url+"'>购买</a><br />");
}
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package org.wuqinghua.java.ch03;
import java.util.HashMap;
import java.util.Map;
import java.util.StringJoiner;
/**
* Created by wuqinghua on 17/6/30.
* 模拟数据库
*/
public class DB {
private static Map<String, Book> map = new HashMap<>();
static {
map.put("1", new Book("1", "javaweb开发"));
map.put("2", new Book("2", "spring开发"));
map.put("3", new Book("3", "hibernate开发"));
map.put("4", new Book("4", "struts开发"));
map.put("5", new Book("5", "ajax开发"));
}
public static Map<String,Book> getAll(){
return map;
}
public static Book get(String id){
return map.get(id);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package org.wuqinghua.java.ch03;
/**
* Created by wuqinghua on 17/6/30.
*/
public class Book {
private String id;
private String name;
public Book() {
}
public Book(String id, String name) {
this.id = id;
this.name = name;
}
public String getId() {
return id;
}
public void setId(String id) {
this.id = id;
}
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
package org.wuqinghua.java.ch03;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.util.ArrayList;
import java.util.List;
/**
* Created by wuqinghua on 17/6/30.
*/
@WebServlet(urlPatterns = "/buyServlet")
public class BuyServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String id = req.getParameter("id");
Book book = DB.get(id);//得到想要购买的书
HttpSession session = req.getSession();
List<Book> books = (List<Book>) session.getAttribute("books");
if(books==null){
books = new ArrayList<>();
session.setAttribute("books",books);
}
books.add(book);
String url = resp.encodeRedirectURL(req.getContextPath()+"/listCartServlet");
System.out.println(url);
resp.sendRedirect(url);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
package org.wuqinghua.java.ch03;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import javax.servlet.http.HttpSession;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.List;
/**
* Created by wuqinghua on 17/6/30.
*/
@WebServlet(urlPatterns = "/listCartServlet")
public class ListCartServlet extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setCharacterEncoding("UTF-8");
resp.setContentType("text/html;charset=UTF-8");
PrintWriter out = resp.getWriter();
HttpSession session = req.getSession();
List<Book> books = (List<Book>) session.getAttribute("books");
if(books==null || books.size()==0){
out.print("对不起,您没有购买任何产品。");
return;
}
//显示用户的购买纪录
out.write("您买过如下产品:<br />");
for (Book book: books) {
out.write(book.getName()+"<br />");
}
}
}

在禁用了cookie的IE8下的运行效果如下:

  演示效果

  通过查看IndexServlet生成的html代码可以看到,每一个超链接后面都带上了session的Id,如下所示

1
2
3
4
5
本网站有如下书:<br/>javaweb开发 <a href='/JavaWeb_Session_Study_20140720/servlet/BuyServlet;jsessionid=96BDFB9D87A08D5AB1EAA2537CDE2DB2?id=1'>购买</a><br/>
spring开发 <a href='/JavaWeb_Session_Study_20140720/servlet/BuyServlet;jsessionid=96BDFB9D87A08D5AB1EAA2537CDE2DB2?id=2'>购买</a><br/>
hibernate开发 <a href='/JavaWeb_Session_Study_20140720/servlet/BuyServlet;jsessionid=96BDFB9D87A08D5AB1EAA2537CDE2DB2?id=3'>购买</a><br/>
struts开发 <a href='/JavaWeb_Session_Study_20140720/servlet/BuyServlet;jsessionid=96BDFB9D87A08D5AB1EAA2537CDE2DB2?id=4'>购买</a><br/>
ajax开发 <a href='/JavaWeb_Session_Study_20140720/servlet/BuyServlet;jsessionid=96BDFB9D87A08D5AB1EAA2537CDE2DB2?id=5'>购买</a><br/>

  所以,当浏览器禁用了cookie后,就可以用URL重写这种解决方案解决Session数据共享问题。而且response. encodeRedirectURL(java.lang.String url) 和response. encodeURL(java.lang.String url)是两个非常智能的方法,当检测到浏览器没有禁用cookie时,那么就不进行URL重写了。我们在没有禁用cookie的火狐浏览器下访问,效果如下:

img

  从演示动画中可以看到,浏览器第一次访问时,服务器创建Session,然后将Session的Id以Cookie的形式发送回给浏览器,response. encodeURL(java.lang.String url)方法也将URL进行了重写,当点击刷新按钮第二次访问,由于火狐浏览器没有禁用cookie,所以第二次访问时带上了cookie,此时服务器就可以知道当前的客户端浏览器并没有禁用cookie,那么就通知response. encodeURL(java.lang.String url)方法不用将URL进行重写了。

五、session对象的创建和销毁时机

5.1、session对象的创建时机

  在程序中第一次调用request.getSession()方法时就会创建一个新的Session,可以用isNew()方法来判断Session是不是新创建的

范例:创建session

1
2
3
4
5
6
7
8
9
10
//使用request对象的getSession()获取session,如果session不存在则创建一个
HttpSession session = request.getSession();
//获取session的Id
String sessionId = session.getId();
//判断session是不是新创建的
if (session.isNew()) {
response.getWriter().print("session创建成功,session的id是:"+sessionId);
}else {
response.getWriter().print("服务器已经存在session,session的id是:"+sessionId);
}

5.2、session对象的销毁时机

  session对象默认30分钟没有使用,则服务器会自动销毁session,在web.xml文件中可以手工配置session的失效时间,例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
<?xml version="1.0" encoding="UTF-8"?>
<web-app version="2.5"
xmlns="http://java.sun.com/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/javaee
http://java.sun.com/xml/ns/javaee/web-app_2_5.xsd">
<display-name></display-name>
<welcome-file-list>
<welcome-file>index.jsp</welcome-file>
</welcome-file-list>
<!-- 设置Session的有效时间:以分钟为单位-->
<session-config>
<session-timeout>15</session-timeout>
</session-config>
</web-app>

  当需要在程序中手动设置Session失效时,可以手工调用session.invalidate方法,摧毁session。

1
2
3
HttpSession session = request.getSession();
//手工调用session.invalidate方法,摧毁session
session.invalidate();

JavaWeb学习总结(十一)--使用Cookie进行会话管理

发表于 2017-06-30 | 分类于 Java Web |

[TOC]

一、会话的概念

  会话可简单理解为:用户开一个浏览器,点击多个超链接,访问服务器多个web资源,然后关闭浏览器,整个过程称之为一个会话。
  有状态会话:一个同学来过教室,下次再来教室,我们会知道这个同学曾经来过,这称之为有状态会话。

二、会话过程中要解决的一些问题?

  每个用户在使用浏览器与服务器进行会话的过程中,不可避免各自会产生一些数据,程序要想办法为每个用户保存这些数据。

三、保存会话数据的两种技术

3.1、Cookie

  Cookie是客户端技术,程序把每个用户的数据以cookie的形式写给用户各自的浏览器。当用户使用浏览器再去访问服务器中的web资源时,就会带着各自的数据去。这样,web资源处理的就是用户各自的数据了。

3.2、Session

  Session是服务器端技术,利用这个技术,服务器在运行时可以为每一个用户的浏览器创建一个其独享的session对象,由于session为用户浏览器独享,所以用户在访问服务器的web资源时,可以把各自的数据放在各自的session中,当用户再去访问服务器中的其它web资源时,其它web资源再从用户各自的session中取出数据为用户服务。

四、Java提供的操作Cookie的API

  Java中的javax.servlet.http.Cookie类用于创建一个Cookie

Cookie类的主要方法
No. 方法 类型 描述
1 Cookie(String name, String value) 构造方法 实例化Cookie对象,传入cooke名称和cookie的值
2 public String getName() 普通方法 取得Cookie的名字
3 public String getValue() 普通方法 取得Cookie的值
4 public void setValue(String newValue) 普通方法 设置Cookie的值
5 public void setMaxAge(int expiry) 普通方法 设置Cookie的最大保存时间,即cookie的有效期,当服务器给浏览器回送一个cookie时,如果在服务器端没有调用setMaxAge方法设置cookie的有效期,那么cookie的有效期只在一次会话过程中有效,用户开一个浏览器,点击多个超链接,访问服务器多个web资源,然后关闭浏览器,整个过程称之为一次会话,当用户关闭浏览器,会话就结束了,此时cookie就会失效,如果在服务器端使用setMaxAge方法设置了cookie的有效期,比如设置了30分钟,那么当服务器把cookie发送给浏览器时,此时cookie就会在客户端的硬盘上存储30分钟,在30分钟内,即使浏览器关了,cookie依然存在,在30分钟内,打开浏览器访问服务器时,浏览器都会把cookie一起带上,这样就可以在服务器端获取到客户端浏览器传递过来的cookie里面的信息了,这就是cookie设置maxAge和不设置maxAge的区别,不设置maxAge,那么cookie就只在一次会话中有效,一旦用户关闭了浏览器,那么cookie就没有了,那么浏览器是怎么做到这一点的呢,我们启动一个浏览器,就相当于启动一个应用程序,而服务器回送的cookie首先是存在浏览器的缓存中的,当浏览器关闭时,浏览器的缓存自然就没有了,所以存储在缓存中的cookie自然就被清掉了,而如果设置了cookie的有效期,那么浏览器在关闭时,就会把缓存中的cookie写到硬盘上存储起来,这样cookie就能够一直存在了。
6 public int getMaxAge() 普通方法 获取Cookies的有效期
7 public void setPath(String uri) 普通方法 设置cookie的有效路径,比如把cookie的有效路径设置为”/xdp”,那么浏览器访问”xdp”目录下的web资源时,都会带上cookie,再比如把cookie的有效路径设置为”/xdp/gacl”,那么浏览器只有在访问”xdp”目录下的”gacl”这个目录里面的web资源时才会带上cookie一起访问,而当访问”xdp”目录下的web资源时,浏览器是不带cookie的
8 public String getPath() 普通方法 获取cookie的有效路径
9 public void setDomain(String pattern) 普通方法 设置cookie的有效域
10 public String getDomain() 普通方法 获取cookie的有效域

  response接口也中定义了一个addCookie方法,它用于在其响应头中增加一个相应的Set-Cookie头字段。 同样,request接口中也定义了一个getCookies方法,它用于获取客户端提交的Cookie。

五、Cookie使用范例

5.1、使用cookie记录用户上一次访问的时间

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
package org.wuqinghua.java.ch02;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Date;
/**
* Created by wuqinghua on 17/6/30.
*/
@WebServlet(urlPatterns = {"/cookieDemo01"})
public class CookieDemo01 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//设置服务器以UTF-8编码进行输出
resp.setCharacterEncoding("utf-8");
//设置浏览器以UTF-8进行接受,解决乱码问题
resp.setContentType("text/html;charset=UTF-8");
PrintWriter writer = resp.getWriter();
//获取浏览器访问服务器时传递的cookie
Cookie[] cookies = req.getCookies();
//如果是第一次访问那么cookies为null
if (cookies!=null){
writer.print("您上次访问的时间为:");
for (Cookie cookie: cookies) {
if(cookie.getName().equals("lastAccessTime")){
long lastAccessTime = Long.parseLong(cookie.getValue());
Date date = new Date(lastAccessTime);
writer.write(date.toLocaleString());
}
}
}else{
writer.write("您是第一次访问本站.");
}
//用户访问之后重新设置用户的访问时间,存储到cookie中,然后在发送给浏览器
Cookie cookie = new Cookie("lastAccessTime",System.currentTimeMillis()+"");
//将cookie添加到resp对象中
resp.addCookie(cookie);
}
}

第一次访问时这个Servlet时,效果如下所示:

img

点击浏览器的刷新按钮,进行第二次访问,此时就服务器就可以通过cookie获取浏览器上一次访问的时间了,效果如下:

img

  在上面的例子中,在程序代码中并没有使用setMaxAge方法设置cookie的有效期,所以当关闭浏览器之后,cookie就失效了,要想在关闭了浏览器之后,cookie依然有效,那么在创建cookie时,就要为cookie设置一个有效期。如下所示:

1
2
3
4
5
6
//用户访问之后重新设置用户的访问时间,存储到cookie中,然后在发送给浏览器
Cookie cookie = new Cookie("lastAccessTime",System.currentTimeMillis()+"");
//设置Cookie有效期为一天
cookie.setMaxAge(24*60*60);
//将cookie添加到resp对象中
resp.addCookie(cookie);

img

  用户第一次访问时,服务器发送给浏览器的cookie就存储到了硬盘上,如下所示:

img

  这样即使关闭了浏览器,下次再访问时,也依然可以通过cookie获取用户上一次访问的时间。

六、Cookie注意细节

  1. 一个Cookie只能标识一种信息,它至少含有一个标识该信息的名称(NAME)和设置值(VALUE)。
  2. 一个WEB站点可以给一个WEB浏览器发送多个Cookie,一个WEB浏览器也可以存储多个WEB站点提供的Cookie。
  3. 浏览器一般只允许存放300个Cookie,每个站点最多存放20个Cookie,每个Cookie的大小限制为4KB。
  4. 如果创建了一个cookie,并将他发送到浏览器,默认情况下它是一个会话级别的cookie(即存储在浏览器的内存中),用户退出浏览器之后即被删除。若希望浏览器将该cookie存储在磁盘上,则需要使用maxAge,并给出一个以秒为单位的时间。将最大时效设为0则是命令浏览器删除该cookie。

6.1、删除Cookie

注意:删除cookie时,path必须一致,否则不会删除

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package org.wuqinghua.java.ch02;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* Created by wuqinghua on 17/6/30.
*/
@WebServlet(urlPatterns = "/cookieDemo02")
public class CookieDemo02 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//创建一个名字为lastAccessTime的cookie
Cookie cookie = new Cookie("lastAccessTime",System.currentTimeMillis()+"");
//将cookie的有效期设置为0,命令浏览器删除该cookie
cookie.setMaxAge(0);
resp.addCookie(cookie);
}
}

6.2、cookie中存取中文

  要想在cookie中存储中文,那么必须使用URLEncoder类里面的encode(String s, String enc)方法进行中文转码,例如:

1
Cookie cookie = new Cookie("userName", URLEncoder.encode("孤傲苍狼", "UTF-8")); response.addCookie(cookie);

  在获取cookie中的中文数据时,再使用URLDecoder类里面的decode(String s, String enc)进行解码,例如:

1
URLDecoder.decode(cookies[i].getValue(), "UTF-8")

JavaWeb学习总结(十)--HttpServletRequest

发表于 2017-06-30 | 分类于 Java Web |

[TOC]

一、HttpServletRequest介绍

  HttpServletRequest对象代表客户端的请求,当客户端通过HTTP协议访问服务器时,HTTP请求头中的所有信息都封装在这个对象中,通过这个对象提供的方法,可以获得客户端请求的所有信息。

二、Request常用方法

2.1、获得客户机信息

  getRequestURL方法返回客户端发出请求时的完整URL。
  getRequestURI方法返回请求行中的资源名部分。
  getQueryString 方法返回请求行中的参数部分。
  getPathInfo方法返回请求URL中的额外路径信息。额外路径信息是请求URL中的位于Servlet的路径之后和查询参数之前的内容,它以“/”开头。
  getRemoteAddr方法返回发出请求的客户机的IP地址。
  getRemoteHost方法返回发出请求的客户机的完整主机名。
  getRemotePort方法返回客户机所使用的网络端口号。
  getLocalAddr方法返回WEB服务器的IP地址。
  getLocalName方法返回WEB服务器的主机名。

范例:通过request对象获取客户端请求信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
package org.wuqinghua.java.ch05;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
/**
* Created by wuqinghua on 17/7/3.
*/
@WebServlet(urlPatterns = "/requestDemo01")
public class RequestDemo01 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
/**
* 获取客户端信息
*/
String requestUrl = req.getRequestURL().toString();//获取请求的url地址
String requestURI = req.getRequestURI();//获取请求的资源
String queryString = req.getQueryString();//获取请求参数中附带的参数
String remoteAddr = req.getRemoteAddr();//获取来访的ip
String remoteHost = req.getRemoteHost();
int remotePort = req.getRemotePort();//获取来访的端口
String remoteUser = req.getRemoteUser();
String method = req.getMethod();//得到请求url使用的方法
String pathInfo = req.getPathInfo();
String localAddr = req.getLocalAddr();//获取服务器的ip地址
String localName = req.getLocalName();//获取服务器的主机名称
resp.setCharacterEncoding("UTF-8");
resp.setContentType("text/html;charset=UTF-8");
PrintWriter out = resp.getWriter();
out.print("获取到的客户机信息如下:");
out.print("<hr />");
out.print("请求的url:" + requestUrl);
out.print("<br />");
out.print("请求的资源:" + requestURI);
out.print("<br />");
out.print("附带的参数:" + queryString);
out.print("<br />");
out.print("来访者的ip地址:" + remoteAddr);
out.print("<br />");
out.print("来访者的主机名称:" + remoteHost);
out.print("<br />");
out.print("来访者的端口:" + remotePort);
out.print("<br />");
out.print("remoteUser:" + remoteUser);
out.print("<br />");
out.print("请求方法:" + method);
out.print("<br />");
out.print("pathInfo:" + pathInfo);
out.print("<br />");
out.print("服务器的ip:" + localAddr);
out.print("<br />");
out.print("服务器的主机:" + localName);
}
}

运行结果:

  img

2.2、获得客户机请求头

  getHeader(string name)方法:String
  getHeaders(String name)方法:Enumeration
  getHeaderNames()方法

范例:通过request对象获取客户端请求头信息

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
package org.wuqinghua.java.ch05;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Enumeration;
/**
* Created by wuqinghua on 17/7/3.
*/
@WebServlet(urlPatterns = "/requestDemo02")
public class ResquestDemo02 extends HttpServlet{
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setCharacterEncoding("utf-8");
resp.setContentType("text/html;charset=utf-8");
PrintWriter out = resp.getWriter();
Enumeration<String> headerNames = req.getHeaderNames();//获取所有的头部
out.print("获取客户端请求头部如下:");
out.print("<hr />");
while (headerNames.hasMoreElements()){
String headName = headerNames.nextElement();
String headerValue = req.getHeader(headName);//根据头部名称获取头部值
out.print(headName+":"+headerValue);
out.print("<br />");
}
out.write("<br/>");
out.write("获取到的客户端Accept-Encoding请求头的值:");
out.write("<hr/>");
String value = req.getHeader("Accept-Encoding");//获取Accept-Encoding请求头对应的值
out.write(value);
Enumeration<String> e = req.getHeaders("Accept-Encoding");
while (e.hasMoreElements()) {
String string = e.nextElement();
System.out.println(string);
}
}
}

运行结果如下:

img

2.3、获得客户机请求参数(客户端提交的数据)

  • getParameter(String)方法(常用)
  • getParameterValues(String name)方法(常用)
  • getParameterNames()方法(不常用)
  • getParameterMap()方法(编写框架时常用)

比如现在有如下的form表单

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>Html的Form表单元素</title>
</head>
<fieldset style="width:500px;">
<legend>Html的Form表单元素</legend>
<!--form表单的action属性规定当提交表单时,向何处发送表单数据,method属性指明表单的提交方式,分为get和post,默认为get-->
<form action="${pageContext.request.contextPath}/servlet/RequestDemo03" method="post">
<!--输入文本框,SIZE表示显示长度,maxlength表示最多输入长度-->
编&nbsp;&nbsp;号(文本框):
<input type="text" name="userid" value="NO." size="2" maxlength="2"><br>
<!--输入文本框,通过value指定其显示的默认值-->
用户名(文本框):<input type="text" name="username" value="请输入用户名"><br>
<!--密码框,其中所有输入的内容都以密文的形式显示-->
密&nbsp;&nbsp;码(密码框):
<!--&nbsp;表示的是一个空格-->
<input type="password" name="userpass" value="请输入密码"><br>
<!--单选按钮,通过checked指定默认选中,名称必须一样,其中value为真正需要的内容-->
性&nbsp;&nbsp;别(单选框):
<input type="radio" name="sex" value="男" checked>男
<input type="radio" name="sex" value="女">女<br>
<!--下拉列表框,通过<option>元素指定下拉的选项-->
部&nbsp;&nbsp;门(下拉框):
<select name="dept">
<option value="技术部">技术部</option>
<option value="销售部" SELECTED>销售部</option>
<option value="财务部">财务部</option>
</select><br>
<!--复选框,可以同时选择多个选项,名称必须一样,其中value为真正需要的内容-->
兴&nbsp;&nbsp;趣(复选框):
<input type="checkbox" name="inst" value="唱歌">唱歌
<input type="checkbox" name="inst" value="游泳">游泳
<input type="checkbox" name="inst" value="跳舞">跳舞
<input type="checkbox" name="inst" value="编程" checked>编程
<input type="checkbox" name="inst" value="上网">上网
<br>
<!--大文本输入框,宽度为34列,高度为5行-->
说&nbsp;&nbsp;明(文本域):
<textarea name="note" cols="34" rows="5">
</textarea>
<br>
<!--隐藏域,在页面上无法看到,专门用来传递参数或者保存参数-->
<input type="hidden" name="hiddenField" value="hiddenvalue"/>
<!--提交表单按钮,当点击提交后,所有填写的表单内容都会被传输到服务器端-->
<input type="submit" value="提交(提交按钮)">
<!--重置表单按钮,当点击重置后,所有表单恢复原始显示内容-->
<input type="reset" value="重置(重置按钮)">
</form>
<!--表单结束-->
</fieldset>
</body>
<!--完结标记-->
</html>
<!--完结标记-->

在Form表单中填写数据,然后提交到RequestDemo03这个Servlet进行处理,填写的表单数据如下:

  img

在服务器端使用getParameter方法和getParameterValues方法接收表单参数,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
package gacl.request.study;
import java.io.IOException;
import java.text.MessageFormat;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @author gacl
* 获取客户端通过Form表单提交上来的参数
*/
public class RequestDemo03 extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
//客户端是以UTF-8编码提交表单数据的,所以需要设置服务器端以UTF-8的编码进行接收,否则对于中文数据就会产生乱码
request.setCharacterEncoding("UTF-8");
/**
* 编&nbsp;&nbsp;号(文本框):
<input type="text" name="userid" value="NO." size="2" maxlength="2">
*/
String userid = request.getParameter("userid");//获取填写的编号,userid是文本框的名字,<input type="text" name="userid">
/**
* 用户名(文本框):<input type="text" name="username" value="请输入用户名">
*/
String username = request.getParameter("username");//获取填写的用户名
/**
* 密&nbsp;&nbsp;码(密码框):<input type="password" name="userpass" value="请输入密码">
*/
String userpass = request.getParameter("userpass");//获取填写的密码
String sex = request.getParameter("sex");//获取选中的性别
String dept = request.getParameter("dept");//获取选中的部门
//获取选中的兴趣,因为可以选中多个值,所以获取到的值是一个字符串数组,因此需要使用getParameterValues方法来获取
String[] insts = request.getParameterValues("inst");
String note = request.getParameter("note");//获取填写的说明信息
String hiddenField = request.getParameter("hiddenField");//获取隐藏域的内容
String instStr="";
/**
* 获取数组数据的技巧,可以避免insts数组为null时引发的空指针异常错误!
*/
for (int i = 0; insts!=null && i < insts.length; i++) {
if (i == insts.length-1) {
instStr+=insts[i];
}else {
instStr+=insts[i]+",";
}
}
String htmlStr = "<table>" +
"<tr><td>填写的编号:</td><td>{0}</td></tr>" +
"<tr><td>填写的用户名:</td><td>{1}</td></tr>" +
"<tr><td>填写的密码:</td><td>{2}</td></tr>" +
"<tr><td>选中的性别:</td><td>{3}</td></tr>" +
"<tr><td>选中的部门:</td><td>{4}</td></tr>" +
"<tr><td>选中的兴趣:</td><td>{5}</td></tr>" +
"<tr><td>填写的说明:</td><td>{6}</td></tr>" +
"<tr><td>隐藏域的内容:</td><td>{7}</td></tr>" +
"</table>";
htmlStr = MessageFormat.format(htmlStr, userid,username,userpass,sex,dept,instStr,note,hiddenField);
response.setCharacterEncoding("UTF-8");//设置服务器端以UTF-8编码输出数据到客户端
response.setContentType("text/html;charset=UTF-8");//设置客户端浏览器以UTF-8编码解析数据
response.getWriter().write(htmlStr);//输出htmlStr里面的内容到客户端浏览器显示
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}

运行结果如下:

  img

在服务器端使用getParameterNames方法接收表单参数,代码如下:

1
2
3
4
5
6
Enumeration<String> paramNames = request.getParameterNames();//获取所有的参数名
while (paramNames.hasMoreElements()) {
String name = paramNames.nextElement();//得到参数名
String value = request.getParameter(name);//通过参数名获取对应的值
System.out.println(MessageFormat.format("{0}={1}", name,value));
}

运行结果如下:

  img

在服务器端使用getParameterMap方法接收表单参数,代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//request对象封装的参数是以Map的形式存储的
Map<String, String[]> paramMap = request.getParameterMap();
for(Map.Entry<String, String[]> entry :paramMap.entrySet()){
String paramName = entry.getKey();
String paramValue = "";
String[] paramValueArr = entry.getValue();
for (int i = 0; paramValueArr!=null && i < paramValueArr.length; i++) {
if (i == paramValueArr.length-1) {
paramValue+=paramValueArr[i];
}else {
paramValue+=paramValueArr[i]+",";
}
}
System.out.println(MessageFormat.format("{0}={1}", paramName,paramValue));
}

运行结果如下:

  img

三、request接收表单提交中文参数乱码问题

3.1、以POST方式提交表单中文参数的乱码问题

例如有如下的form表单页面:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<%--
Created by IntelliJ IDEA.
User: wuqinghua
Date: 17/7/3
Time: 下午9:13
To change this template use File | Settings | File Templates.
--%>
<%@ page contentType="text/html;charset=UTF-8" language="java" isELIgnored="false" %>
<html>
<head>
<title>request接受正文参数乱码</title>
</head>
<body>
<form action="<%=request.getContextPath()%>/requestDemo04" method="post">
用户名: <input type="text" name="userName"/>
<input type="submit" value="post方式提交">
</form>
</body>
</html>

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
package org.wuqinghua.java.ch05;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* Created by wuqinghua on 17/7/3.
*/
@WebServlet(urlPatterns = "/requestDemo04")
public class RequestDemo04 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String userName = req.getParameter("userName");
System.out.println("userName:"+userName);
}
}

运行结果为:

JavaWeb学习总结(九)--通过Servlet生成验证码

发表于 2017-06-30 | 分类于 Java Web |

[TOC]

一、BufferedImage类介绍

生成验证码图片主要用到了一个BufferedImage类,如下:

img

创建一个DrawImage Servlet,用来生成验证码图片

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
package org.wuqinghua.java.ch04;
import javax.imageio.ImageIO;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.Random;
/**
* Created by wuqinghua on 17/7/3.
*/
@WebServlet(urlPatterns = "/drawImage")
public class DrawImage extends HttpServlet {
public static final int WIDTH = 120;//生成的图片的宽度
public static final int HEIGHT = 30;//生成的图片的高度
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String createTypeFlag = req.getParameter("createTypeFlag");
//1.在内存中创建一个图片
BufferedImage image = new BufferedImage(WIDTH, HEIGHT, BufferedImage.TYPE_INT_RGB);
//2.获取到画笔
Graphics2D g = (Graphics2D) image.getGraphics();
//3.设置图片的背景颜色
setBackground(g, Color.WHITE);
//4.设置边框
setBorder(g, Color.BLUE);
//5.在图片上设置干扰线
drawRandomLine(g, Color.GREEN, 5);
//6.在图片上生成随机验证码
String code = drawRandomCode(g, Color.RED, ImageType.getImageType(createTypeFlag));
//7.将验证码存入到session
req.getSession().setAttribute("code", code);
//8.设置响应头
resp.setHeader("content-type", "image/jpeg");
//9.不要缓存
resp.setDateHeader("expries", -1);
resp.setHeader("Cache-Control", "no-cache");
resp.setHeader("Pragma", "no-cache");
//10.将图片写入浏览器
ImageIO.write(image,"jpg",resp.getOutputStream());
}
/**
* 生成随机验证码
*
* @param g
* @param color
* @param imageType
* @return
*/
private String drawRandomCode(Graphics2D g, Color color, ImageType imageType) {
g.setColor(color);
//设置字体
g.setFont(new Font("宋体", Font.BOLD, 20));
//常用的中国汉字
String baseChineseChar = "\u7684\u4e00\u4e86\u662f\u6211\u4e0d\u5728\u4eba\u4eec\u6709\u6765\u4ed6\u8fd9\u4e0a\u7740\u4e2a\u5730\u5230\u5927\u91cc\u8bf4\u5c31\u53bb\u5b50\u5f97\u4e5f\u548c\u90a3\u8981\u4e0b\u770b\u5929\u65f6\u8fc7\u51fa\u5c0f\u4e48\u8d77\u4f60\u90fd\u628a\u597d\u8fd8\u591a\u6ca1\u4e3a\u53c8\u53ef\u5bb6\u5b66\u53ea\u4ee5\u4e3b\u4f1a\u6837\u5e74\u60f3\u751f\u540c\u8001\u4e2d\u5341\u4ece\u81ea\u9762\u524d\u5934\u9053\u5b83\u540e\u7136\u8d70\u5f88\u50cf\u89c1\u4e24\u7528\u5979\u56fd\u52a8\u8fdb\u6210\u56de\u4ec0\u8fb9\u4f5c\u5bf9\u5f00\u800c\u5df1\u4e9b\u73b0\u5c71\u6c11\u5019\u7ecf\u53d1\u5de5\u5411\u4e8b\u547d\u7ed9\u957f\u6c34\u51e0\u4e49\u4e09\u58f0\u4e8e\u9ad8\u624b\u77e5\u7406\u773c\u5fd7\u70b9\u5fc3\u6218\u4e8c\u95ee\u4f46\u8eab\u65b9\u5b9e\u5403\u505a\u53eb\u5f53\u4f4f\u542c\u9769\u6253\u5462\u771f\u5168\u624d\u56db\u5df2\u6240\u654c\u4e4b\u6700\u5149\u4ea7\u60c5\u8def\u5206\u603b\u6761\u767d\u8bdd\u4e1c\u5e2d\u6b21\u4eb2\u5982\u88ab\u82b1\u53e3\u653e\u513f\u5e38\u6c14\u4e94\u7b2c\u4f7f\u5199\u519b\u5427\u6587\u8fd0\u518d\u679c\u600e\u5b9a\u8bb8\u5feb\u660e\u884c\u56e0\u522b\u98de\u5916\u6811\u7269\u6d3b\u90e8\u95e8\u65e0\u5f80\u8239\u671b\u65b0\u5e26\u961f\u5148\u529b\u5b8c\u5374\u7ad9\u4ee3\u5458\u673a\u66f4\u4e5d\u60a8\u6bcf\u98ce\u7ea7\u8ddf\u7b11\u554a\u5b69\u4e07\u5c11\u76f4\u610f\u591c\u6bd4\u9636\u8fde\u8f66\u91cd\u4fbf\u6597\u9a6c\u54ea\u5316\u592a\u6307\u53d8\u793e\u4f3c\u58eb\u8005\u5e72\u77f3\u6ee1\u65e5\u51b3\u767e\u539f\u62ff\u7fa4\u7a76\u5404\u516d\u672c\u601d\u89e3\u7acb\u6cb3\u6751\u516b\u96be\u65e9\u8bba\u5417\u6839\u5171\u8ba9\u76f8\u7814\u4eca\u5176\u4e66\u5750\u63a5\u5e94\u5173\u4fe1\u89c9\u6b65\u53cd\u5904\u8bb0\u5c06\u5343\u627e\u4e89\u9886\u6216\u5e08\u7ed3\u5757\u8dd1\u8c01\u8349\u8d8a\u5b57\u52a0\u811a\u7d27\u7231\u7b49\u4e60\u9635\u6015\u6708\u9752\u534a\u706b\u6cd5\u9898\u5efa\u8d76\u4f4d\u5531\u6d77\u4e03\u5973\u4efb\u4ef6\u611f\u51c6\u5f20\u56e2\u5c4b\u79bb\u8272\u8138\u7247\u79d1\u5012\u775b\u5229\u4e16\u521a\u4e14\u7531\u9001\u5207\u661f\u5bfc\u665a\u8868\u591f\u6574\u8ba4\u54cd\u96ea\u6d41\u672a\u573a\u8be5\u5e76\u5e95\u6df1\u523b\u5e73\u4f1f\u5fd9\u63d0\u786e\u8fd1\u4eae\u8f7b\u8bb2\u519c\u53e4\u9ed1\u544a\u754c\u62c9\u540d\u5440\u571f\u6e05\u9633\u7167\u529e\u53f2\u6539\u5386\u8f6c\u753b\u9020\u5634\u6b64\u6cbb\u5317\u5fc5\u670d\u96e8\u7a7f\u5185\u8bc6\u9a8c\u4f20\u4e1a\u83dc\u722c\u7761\u5174\u5f62\u91cf\u54b1\u89c2\u82e6\u4f53\u4f17\u901a\u51b2\u5408\u7834\u53cb\u5ea6\u672f\u996d\u516c\u65c1\u623f\u6781\u5357\u67aa\u8bfb\u6c99\u5c81\u7ebf\u91ce\u575a\u7a7a\u6536\u7b97\u81f3\u653f\u57ce\u52b3\u843d\u94b1\u7279\u56f4\u5f1f\u80dc\u6559\u70ed\u5c55\u5305\u6b4c\u7c7b\u6e10\u5f3a\u6570\u4e61\u547c\u6027\u97f3\u7b54\u54e5\u9645\u65e7\u795e\u5ea7\u7ae0\u5e2e\u5566\u53d7\u7cfb\u4ee4\u8df3\u975e\u4f55\u725b\u53d6\u5165\u5cb8\u6562\u6389\u5ffd\u79cd\u88c5\u9876\u6025\u6797\u505c\u606f\u53e5\u533a\u8863\u822c\u62a5\u53f6\u538b\u6162\u53d4\u80cc\u7ec6";
//数字和字母的组合
String baseNumLetter = "0123456789ABCDEFGHJKLMNOPQRSTUVWXYZ";
//纯数字
String baseNum = "0123456789";
//纯字母
String baseLetter = "ABCDEFGHJKLMNOPQRSTUVWXYZ";
switch (imageType) {
case CH://汉字
return createRandomChar(g, baseChineseChar);
case NL://数字和字母
return createRandomChar(g, baseNumLetter);
case L:
return createRandomChar(g, baseLetter);
default:
return createRandomChar(g, baseNum);
}
}
/**
* 根据基础字符生成随机字符串
*
* @param g
* @param baseChar
* @return
*/
private String createRandomChar(Graphics2D g, String baseChar) {
StringBuffer buffer = new StringBuffer();
int x = 5;
String ch = "";
//控制字数
for (int i = 0; i < 4; i++) {
//设置字体的旋转角度
int degree = new Random().nextInt() % 30;
ch = baseChar.charAt(new Random().nextInt(baseChar.length())) + "";
buffer.append(ch);
//正向角度
g.rotate(degree * Math.PI / 180, x, 20);
g.drawString(ch, x, 20);
//反向角度
g.rotate(-degree * Math.PI / 180, x, 20);
x += 30;
}
return buffer.toString();
}
/**
* 设置干扰线
*
* @param g
*/
private void drawRandomLine(Graphics2D g, Color color, int lineCount) {
g.setColor(color);
for (int i = 0; i < lineCount; i++) {
int x1 = new Random().nextInt(WIDTH);
int y1 = new Random().nextInt(HEIGHT);
int x2 = new Random().nextInt(WIDTH);
int y2 = new Random().nextInt(HEIGHT);
g.drawLine(x1, y1, x2, y2);
}
}
/**
* 设置边框
*
* @param g
* @param color
*/
private void setBorder(Graphics2D g, Color color) {
g.setColor(color);
g.drawRect(1, 1, WIDTH - 2, HEIGHT - 2);
}
/**
* 设置背景颜色
*
* @param g
*/
private void setBackground(Graphics2D g, Color color) {
g.setColor(color);
g.fillRect(0, 0, WIDTH, HEIGHT);
}
private enum ImageType {
CH("ch"), NL("nl"),
N("n"), L("l");
private String createTypeFlag = null;
ImageType(String createTypeFlag) {
this.createTypeFlag = createTypeFlag;
}
public static ImageType getImageType(String createTypeFlag) {
if (createTypeFlag.equals("ch")) {
return CH;
} else if (createTypeFlag.equals("nl")) {
return NL;
} else if (createTypeFlag.equals("L")) {
return L;
} else {
return N;
}
}
String getValue() {
return this.createTypeFlag;
}
}
}

运行结果如下: img

二、在Form表单中使用验证码图片

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>在Form表单中使用验证码</title>
<script type="text/javascript">
//刷新验证码
function changeImg(){
document.getElementById("validateCodeImg").src="${pageContext.request.contextPath}/servlet/DrawImage?"+Math.random();
}
</script>
</head>
<body>
<form action="${pageContext.request.contextPath}/servlet/CheckServlet" method="post">
验证码:<input type="text" name="validateCode"/>
<img alt="验证码看不清,换一张" src="${pageContext.request.contextPath}/servlet/DrawImage" id="validateCodeImg" onclick="changeImg()">
<a href="javascript:void(0)" onclick="changeImg()">看不清,换一张</a>
<br/>
<input type="submit" value="提交">
</form>
</body>
</html>

运行结果:

  img

  DrawImage Servlet除了可以生成的字母和数字的组合的验证码图片之外,还可以生成汉字,纯数字,纯字母的验证码图片,只需要向DrawImage Servlet传递约定好的生成标识符参数即可,如下所示:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
<%@ page language="java" import="java.util.*" pageEncoding="UTF-8"%>
<!DOCTYPE HTML PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN">
<html>
<head>
<title>在Form表单中使用验证码</title>
<script type="text/javascript">
//刷新验证码
function changeImg(obj,createTypeFlag){
document.getElementById(obj.id).src="${pageContext.request.contextPath}/servlet/DrawImage?createTypeFlag="+createTypeFlag+"&"+Math.random();
}
</script>
</head>
<body>
<form action="${pageContext.request.contextPath}/servlet/CheckServlet" method="post">
数字字母混合验证码:<input type="text" name="validateCode"/>
<img alt="验证码看不清,换一张" src="${pageContext.request.contextPath}/servlet/DrawImage" id="validateCodeImg1" onclick="changeImg(this,'nl')">
<br/>
中文验证码:<input type="text" name="validateCode"/>
<img alt="验证码看不清,换一张" src="${pageContext.request.contextPath}/servlet/DrawImage?createTypeFlag=ch" id="validateCodeImg2" onclick="changeImg(this,'ch')">
<br/>
英文验证码:<input type="text" name="validateCode"/>
<img alt="验证码看不清,换一张" src="${pageContext.request.contextPath}/servlet/DrawImage?createTypeFlag=l" id="validateCodeImg3" onclick="changeImg(this,'l')">
<br/>
数字验证码:<input type="text" name="validateCode"/>
<img alt="验证码看不清,换一张" src="${pageContext.request.contextPath}/servlet/DrawImage?createTypeFlag=n" id="validateCodeImg4" onclick="changeImg(this,'n')">
<br/>
<input type="submit" value="提交">
</form>
</body>
</html>

运行结果如下:

  img

三、服务器端对form表单提交上来的验证码处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
package gacl.response.study;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @author gacl
* 服务器端接收到验证码后的处理
*/
public class CheckServlet extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String clientCheckcode = request.getParameter("validateCode");//接收客户端浏览器提交上来的验证码
String serverCheckcode = (String) request.getSession().getAttribute("checkcode");//从服务器端的session中取出验证码
if (clientCheckcode.equals(serverCheckcode)) {//将客户端验证码和服务器端验证比较,如果相等,则表示验证通过
System.out.println("验证码验证通过!");
}else {
System.out.println("验证码验证失败!");
}
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}

JavaWeb学习总结(七)--HttpServletResponse(二)

发表于 2017-06-30 | 分类于 Java Web |

[TOC]

一、HttpServletResponse常见应用——生成验证码

1.1、生成随机图片用作验证码

  生成图片主要用到了一个BufferedImage类,

  img

生成随机图片范例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
package org.wuqinghua.java.ch04;
import javax.imageio.ImageIO;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.awt.*;
import java.awt.image.BufferedImage;
import java.io.IOException;
import java.util.Random;
/**
* Created by wuqinghua on 17/7/2.
*/
@WebServlet(urlPatterns = "/responseDemo05")
public class ResponseDemo05 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//设置浏览器每隔5秒刷新一次
resp.setHeader("refresh", "5");
//1.在内存中创建一张图片
BufferedImage image = new BufferedImage(100, 25, BufferedImage.TYPE_INT_RGB);
//2.绘制图片 获取画笔
Graphics2D g = (Graphics2D) image.getGraphics();
g.setColor(Color.WHITE);//设置图片的背景色
g.fillRect(0, 0, 100, 25);//填充背景
//3.向图片上写数值
g.setColor(Color.BLUE);
g.setFont(new Font(null, Font.BOLD, 20));
g.drawString(makeNum(),0,20);
//4.设置浏览器以图片方式打开
resp.setHeader("content-type","image/jpeg");
//5.设置浏览器不缓存图片
resp.setDateHeader("expries",-1);
resp.setHeader("Cache-Control","no-cache");
resp.setHeader("Pragma","no-cache");
//6.将图片写到浏览器
ImageIO.write(image,"jpg",resp.getOutputStream());
}
private String makeNum() {
Random random = new Random();
String num = random.nextInt(9999999) + "";
StringBuffer buffer = new StringBuffer();
for (int i = 0; i < 7 - num.length(); i++) {
buffer.append("0");
}
num = buffer.toString() + num;
return num;
}
}

运行结果如下:

  img

二、HttpServletResponse常见应用——设置响应头控制浏览器的行为

2.1、设置http响应头控制浏览器禁止缓存当前文档内容

1
2
3
response.setDateHeader("expries", -1);
response.setHeader("Cache-Control", "no-cache");
response.setHeader("Pragma", "no-cache");

2.2、设置http响应头控制浏览器定时刷新网页(refresh)

1
response.setHeader("refresh", "5");//设置refresh响应头控制浏览器每隔5秒钟刷新一次

2.3、通过response实现请求重定向

  请求重定向指:一个web资源收到客户端请求后,通知客户端去访问另外一个web资源,这称之为请求重定向。

  应用场景:用户登陆,用户首先访问登录页面,登录成功后,就会跳转到某个页面,这个过程就是一个请求重定向的过程

  实现方式:response.sendRedirect(String location),即调用response对象的sendRedirect方法实现请求重定向
  sendRedirect内部的实现原理:使用response设置**302状态码和设置location响应头实现重定向**

例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package gacl.response.study;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class ResponseDemo04 extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
/**
* 1.调用sendRedirect方法实现请求重定向,
* sendRedirect方法内部调用了
* response.setHeader("Location", "/JavaWeb_HttpServletResponse_Study_20140615/index.jsp");
* response.setStatus(HttpServletResponse.SC_FOUND);//设置302状态码,等同于response.setStatus(302);
*/
response.sendRedirect("/JavaWeb_HttpServletResponse_Study_20140615/index.jsp");
//2.使用response设置302状态码和设置location响应头实现重定向实现请求重定向
//response.setHeader("Location", "/JavaWeb_HttpServletResponse_Study_20140615/index.jsp");
//response.setStatus(HttpServletResponse.SC_FOUND);//设置302状态码,等同于response.setStatus(302);
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}

三、response细节问题

  getOutputStream和getWriter方法分别用于得到输出二进制数据、输出文本数据的ServletOuputStream、Printwriter对象。
  getOutputStream和getWriter这两个方法互相排斥,调用了其中的任何一个方法后,就不能再调用另一方法。
  Servlet程序向ServletOutputStream或PrintWriter对象中写入的数据将被Servlet引擎从response里面获取,Servlet引擎将这些数据当作响应消息的正文,然后再与响应状态行和各响应头组合后输出到客户端。
  Serlvet的service方法结束后,Servlet引擎将检查getWriter或getOutputStream方法返回的输出流对象是否已经调用过close方法,如果没有,Servlet引擎将调用close方法关闭该输出流对象。

JavaWeb学习总结(七)--HttpServletResponse(一)

发表于 2017-06-30 | 分类于 Java Web |

[TOC]

Web服务器收到客户端的http请求,会针对每一次请求,分别创建一个用于代表请求的request对象、和代表响应的response对象。
request和response对象即然代表请求和响应,那我们要获取客户机提交过来的数据,只需要找request对象就行了。要向客户机输出数据,只需要找response对象就行了。

一、HttpServletResponse对象介绍

  img

  HttpServletResponse对象代表服务器的响应。这个对象中封装了向客户端发送数据、发送响应头,发送响应状态码的方法。查看HttpServletResponse的API,可以看到这些相关的方法。

1.1、负责向客户端(浏览器)发送数据的相关方法

  img

1.2、负责向客户端(浏览器)发送响应头的相关方法

  img

  img

1.3、负责向客户端(浏览器)发送响应状态码的相关方法

  img

1.4、响应状态码的常量

  HttpServletResponse定义了很多状态码的常量(具体可以查看Servlet的API),当需要向客户端发送响应状态码时,可以使用这些常量,避免了直接写数字,常见的状态码对应的常量:

  状态码404对应的常量

  img

  状态码200对应的常量

  img

  状态码500对应的常量

  img

二、HttpServletResponse对象常见应用

2.1、使用OutputStream流向客户端浏览器输出中文数据

使用OutputStream流输出中文注意问题:

  在服务器端,数据是以哪个码表输出的,那么就要控制客户端浏览器以相应的码表打开,比如:outputStream.write(“中国”.getBytes(“UTF-8”));使用OutputStream流向客户端浏览器输出中文,以UTF-8的编码进行输出,此时就要控制客户端浏览器以UTF-8的编码打开,否则显示的时候就会出现中文乱码,那么在服务器端如何控制客户端浏览器以以UTF-8的编码显示数据呢?可以通过设置响应头控制浏览器的行为,例如:response.setHeader(“content-type”, “text/html;charset=UTF-8”);通过设置响应头控制浏览器以UTF-8的编码显示数据。

范例:使用OutputStream流向客户端浏览器输出”中国”这两个汉字

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
package org.wuqinghua.java.ch04;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.nio.charset.Charset;
/**
* Created by wuqinghua on 17/7/1.
*/
@WebServlet(urlPatterns = "/responseDemo01")
public class ResponseDemo01 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String data = "中国";
ServletOutputStream outputStream = resp.getOutputStream();//获取输出流
resp.setContentType("text/html;charset=UTF-8");//通过响应头告诉浏览器需要使用utf-8显示数据
/**
* data.getBytes()是一个将字符串转换为字节数组的过程,这个过程一定会查询码表,
* 如果是中文操作系统,默认就会查询GB2312的码表,
* 将字符串转换成字节数组的过程就是将中文字符转换为GB2312上对应的数字
* 比如: "中" 在GB2312上对应的数字为98
* "国" 为99
*/
byte[] bytes = data.getBytes(Charset.forName("UTF-8"));
outputStream.write(bytes);
}
}

运行结果如下:

img

  客户端浏览器接收到数据后,就按照响应头上设置的字符编码来解析数据,如下所示:

  img

2.2、使用PrintWriter流向客户端浏览器输出中文数据

使用PrintWriter流输出中文注意问题:

  在获取PrintWriter输出流之前首先使用”response.setCharacterEncoding(charset)”设置字符以什么样的编码输出到浏览器,如:response.setCharacterEncoding(“UTF-8”);设置将字符以”UTF-8”编码输出到客户端浏览器,然后再使用response.getWriter();获取PrintWriter输出流,这两个步骤不能颠倒,如下:

1
2
3
4
5
6
response.setCharacterEncoding("UTF-8");//设置将字符以"UTF-8"编码输出到客户端浏览器
/**
* PrintWriter out = response.getWriter();这句代码必须放在response.setCharacterEncoding("UTF-");之后
* 否则response.setCharacterEncoding("UTF-8")这行代码的设置将无效,浏览器显示的时候还是乱码
*/
PrintWriter out = response.getWriter();//获取PrintWriter输出流

  然后再使用response.setHeader(“content-type”, “text/html;charset=字符编码”);设置响应头,控制浏览器以指定的字符编码编码进行显示,例如:

1
2
//通过设置响应头控制浏览器以UTF-8的编码显示数据,如果不加这句话,那么浏览器显示的将是乱码
response.setHeader("content-type", "text/html;charset=UTF-8");

  除了可以使用response.setHeader(“content-type”, “text/html;charset=字符编码”);设置响应头来控制浏览器以指定的字符编码编码进行显示这种方式之外,还可以用如下的方式来模拟响应头的作用

1
2
3
4
5
6
/**
* 多学一招:使用HTML语言里面的<meta>标签来控制浏览器行为,模拟通过设置响应头控制浏览器行为
*response.getWriter().write("<meta http-equiv='content-type' content='text/html;charset=UTF-8'/>");
* 等同于response.setHeader("content-type", "text/html;charset=UTF-8");
*/
response.getWriter().write("<meta http-equiv='content-type' content='text/html;charset=UTF-8'/>");

范例:使用PrintWriter流向客户端浏览器输出”中国”这两个汉字

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package org.wuqinghua.java.ch04;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
/**
* Created by wuqinghua on 17/7/2.
*/
@WebServlet(urlPatterns = "/responseDemo02")
public class ResponseDemo02 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String data = "中国";
resp.setCharacterEncoding("UTF-8");
resp.setContentType("text/html;charset=utf-8");
PrintWriter out = resp.getWriter();
out.print(data);
}
}

  当需要向浏览器输出字符数据时,使用PrintWriter比较方便,省去了将字符转换成字节数组那一步。

2.3、使用OutputStream或者PrintWriter向客户端浏览器输出数字

比如有如下的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
package org.wuqinghua.java.ch04;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.OutputStream;
/**
* Created by wuqinghua on 17/7/2.
*/
@WebServlet(urlPatterns = "/responseDemo03")
public class ResponseDemo03 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setHeader("content-type","text/html;charset=utf-8");
OutputStream outputStream = resp.getOutputStream();
outputStream.write("使用outputStream向界面输出1:".getBytes("UTF-8"));
outputStream.write(1);
}
}

运行上面代码显示的结果如下: img

  运行的结果和我们想象中的不一样,数字1没有输出来,下面我们修改一下上面的outputOneByOutputStream方法的代码,修改后的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
package org.wuqinghua.java.ch04;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.OutputStream;
/**
* Created by wuqinghua on 17/7/2.
*/
@WebServlet(urlPatterns = "/responseDemo03")
public class ResponseDemo03 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setHeader("content-type","text/html;charset=utf-8");
OutputStream outputStream = resp.getOutputStream();
outputStream.write("使用outputStream向界面输出1:".getBytes("UTF-8"));
// outputStream.write(1);
outputStream.write((1+"").getBytes("UTF-8"));
}
}

1+””这一步是将数字1和一个空字符串相加,这样处理之后,数字1就变成了字符串1了,然后再将字符串1转换成字节数组使用OutputStream进行输出,此时看到的结果如下:

  img

  这次可以看到输出来的1了,这说明了一个问题:在开发过程中,如果希望服务器输出什么浏览器就能看到什么,那么在服务器端都要以字符串的形式进行输出。

  如果使用PrintWriter流输出数字,那么也要先将数字转换成字符串后再输出,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
/**
* 使用PrintWriter流输出数字1
* @param request
* @param response
* @throws IOException
*/
public void outputOneByPrintWriter(HttpServletResponse response) throws IOException{
response.setHeader("content-type", "text/html;charset=UTF-8");
response.setCharacterEncoding("UTF-8");
PrintWriter out = response.getWriter();//获取PrintWriter输出流
out.write("使用PrintWriter流输出数字1:");
out.write(1+"");
}

2.4、文件下载

  文件下载功能是web开发中经常使用到的功能,使用HttpServletResponse对象就可以实现文件的下载

文件下载功能的实现思路:

  1.获取要下载的文件的绝对路径

  2.获取要下载的文件名

  3.设置content-disposition响应头控制浏览器以下载的形式打开文件

  4.获取要下载的文件输入流

  5.创建数据缓冲区

  6.通过response对象获取OutputStream流

  7.将FileInputStream流写入到buffer缓冲区

  8.使用OutputStream将缓冲区的数据输出到客户端浏览器

范例:使用Response实现文件下载

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
package org.wuqinghua.java.ch04;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
/**
* Created by wuqinghua on 17/7/2.
*/
@WebServlet(urlPatterns = "/responseDemo04")
public class ResponseDemo04 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//1.获取绝对路径
String realPath = this.getServletContext().getRealPath("/img/WP_20131005_002.jpg");
//2.获取文件的名称
String filename = realPath.substring(realPath.lastIndexOf("\\") + 1);
//3.设置响应头content-disposition控制浏览器以下载的方式打开文件
resp.setHeader("content-disposition","attachment;filename="+filename);
//4.获取文件的输入流
InputStream in = new FileInputStream(realPath);
//5.获取输出流
OutputStream outputStream = resp.getOutputStream();
//6.将输入流写入到输出流中
byte[] buffer = new byte[4096];
int len = -1;
while ((len=in.read(buffer,0,4096))!=-1){
outputStream.write(buffer,0,len);
}
outputStream.close();
}
}

运行结果如下所示:

  img

范例:**使用Response实现中文文件下载**

  下载中文文件时,需要注意的地方就是中文文件名要使用URLEncoder.encode方法进行编码(URLEncoder.encode(fileName, “字符编码”)),否则会出现文件名乱码。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
package gacl.response.study;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.URLEncoder;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @author gacl
* 文件下载
*/
public class ResponseDemo02 extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
downloadChineseFileByOutputStream(response);//下载中文文件
}
/**
* 下载中文文件,中文文件下载时,文件名要经过URL编码,否则会出现文件名乱码
* @param response
* @throws FileNotFoundException
* @throws IOException
*/
private void downloadChineseFileByOutputStream(HttpServletResponse response)
throws FileNotFoundException, IOException {
String realPath = this.getServletContext().getRealPath("/download/张家界国家森林公园.JPG");//获取要下载的文件的绝对路径
String fileName = realPath.substring(realPath.lastIndexOf("\\")+1);//获取要下载的文件名
//设置content-disposition响应头控制浏览器以下载的形式打开文件,中文文件名要使用URLEncoder.encode方法进行编码,否则会出现文件名乱码
response.setHeader("content-disposition", "attachment;filename="+URLEncoder.encode(fileName, "UTF-8"));
InputStream in = new FileInputStream(realPath);//获取文件输入流
int len = 0;
byte[] buffer = new byte[1024];
OutputStream out = response.getOutputStream();
while ((len = in.read(buffer)) > 0) {
out.write(buffer,0,len);//将缓冲区的数据输出到客户端浏览器
}
in.close();
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}

运行结果如下所示:

  img

  文件下载注意事项:编写文件下载功能时推荐使用OutputStream流,避免使用PrintWriter流,因为OutputStream流是字节流,可以处理任意类型的数据,而PrintWriter流是字符流,只能处理字符数据,如果用字符流处理字节数据,会导致数据丢失。

范例:使用PrintWriter流下载文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
package gacl.response.study;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.FileReader;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.io.PrintWriter;
import java.net.URLEncoder;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* @author gacl
* 文件下载
*/
public class ResponseDemo02 extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
downloadFileByPrintWriter(response);//下载文件,通过PrintWriter流
}
/**
* 下载文件,通过PrintWriter流,虽然也能够实现下载,但是会导致数据丢失,因此不推荐使用PrintWriter流下载文件
* @param response
* @throws FileNotFoundException
* @throws IOException
*/
private void downloadFileByPrintWriter(HttpServletResponse response)
throws FileNotFoundException, IOException {
String realPath = this.getServletContext().getRealPath("/download/张家界国家森林公园.JPG");//获取要下载的文件的绝对路径
String fileName = realPath.substring(realPath.lastIndexOf("\\")+1);//获取要下载的文件名
//设置content-disposition响应头控制浏览器以下载的形式打开文件,中文文件名要使用URLEncoder.encode方法进行编码
response.setHeader("content-disposition", "attachment;filename="+URLEncoder.encode(fileName, "UTF-8"));
FileReader in = new FileReader(realPath);
int len = 0;
char[] buffer = new char[1024];
PrintWriter out = response.getWriter();
while ((len = in.read(buffer)) > 0) {
out.write(buffer,0,len);//将缓冲区的数据输出到客户端浏览器
}
in.close();
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}

运行结果如下:

  img

正常弹出下载框,此时我们点击【保存】按钮将文件下载下来,如下所示:

  img

  可以看到,只下载了5.25MB,而这张图片的原始大小却是

  img

  这说明在下载的时候数据丢失了,所以下载不完全,所以这张图片虽然能够正常下载下来,但是却是无法打开的,因为丢失掉了部分数据,如下所示:

  img

  所以使用PrintWriter流处理字节数据,会导致数据丢失,这一点千万要注意,因此在编写下载文件功能时,要使用OutputStream流,避免使用PrintWriter流,因为OutputStream流是字节流,可以处理任意类型的数据,而PrintWriter流是字符流,只能处理字符数据,如果用字符流处理字节数据,会导致数据丢失。

JavaWeb学习总结(六)--Servlet开发(二)

发表于 2017-06-30 | 分类于 Java Web |

[TOC]

一、ServletConfig讲解

1.1、通过ServletConfig获取Servlet的初始化参数

  当servlet配置了初始化参数后,web容器在创建servlet实例对象时,会自动将这些初始化参数封装到ServletConfig对象中,并在调用servlet的init方法时,将ServletConfig对象传递给servlet。进而,我们通过ServletConfig对象就可以得到当前servlet的初始化参数信息。

​ 例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
package org.wuqinghua.java.ch01;
import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebInitParam;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.Enumeration;
/**
* Created by wuqinghua on 17/6/30.
*/
@WebServlet(name = "servletConfigDemo01",urlPatterns = {"/servletConfigDemo01"},initParams
={@WebInitParam(name = "name",value = "gacl"),@WebInitParam(name = "password",value =
"123"),@WebInitParam(name = "charset",value = "UTF-8")} )
public class ServletConfigDemo01 extends HttpServlet {
//定义ServletConfig对象来接受配置的初始化参数
private ServletConfig servletConfig;
/**
*当servlet配置了初始化参数后,web容器在创建servlet实例对象时,
* 会自动将这些初始化参数封装到ServletConfig对象中,并在调用servlet的init方法时,
* 将servletConfig对象传递给servlet。进而,通过ServletConfig对象就可以得到初始化数据
*/
@Override
public void init(ServletConfig config) throws ServletException {
this.servletConfig = config;
}
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获取配置的参数
String paramVal = this.servletConfig.getInitParameter("name");
resp.getWriter().print(paramVal);
resp.getWriter().print("<hr />");
//获取所有初始化参数
Enumeration<String> e = this.servletConfig.getInitParameterNames();
while (e.hasMoreElements()){
String name = e.nextElement();
String value = this.servletConfig.getInitParameter(name);
resp.getWriter().print(name+"-"+value+"<br />");
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doGet(req, resp);
}
}

运行结果如下:

  img

二、ServletContext对象

  WEB容器在启动时,它会为每个WEB应用程序都创建一个对应的ServletContext对象,它代表当前web应用。
  ServletConfig对象中维护了ServletContext对象的引用,开发人员在编写servlet时,可以通过ServletConfig.getServletContext方法获得ServletContext对象。
  由于一个WEB应用中的所有Servlet共享同一个ServletContext对象,因此Servlet对象之间可以通过ServletContext对象来实现通讯。ServletContext对象通常也被称之为context域对象。

三、ServletContext的应用

  3.1、多个Servlet通过ServletContext对象实现数据共享

  范例:ServletContextDemo1和ServletContextDemo2通过ServletContext对象实现数据共享

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
package org.wuqinghua.java.ch01;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebInitParam;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* Created by wuqinghua on 17/6/30.
*/
@WebServlet(urlPatterns = "/servletContextDemo01",initParams = @WebInitParam(name = "name",value
= "hello"))
public class ServletContextDemo01 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String data = "xdp_gacl";
/**
* 获取ServletContext
*/
ServletContext servletContext =
this.getServletConfig().getServletContext();
ServletContext servletContext1 = this.getServletContext();
System.out.println(servletContext == servletContext1);
System.out.println(this.getServletConfig().getInitParameter("name"));
servletContext.setAttribute("data", data);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package org.wuqinghua.java.ch01;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* Created by wuqinghua on 17/6/30.
*/
@WebServlet(urlPatterns = "/servletContextDemo02")
public class ServletContextDemo02 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
ServletContext servletContext = this.getServletContext();
String data = (String) servletContext.getAttribute("data");
resp.getWriter().print("data="+data);
}
}

先运行ServletContextDemo1,将数据data存储到ServletContext对象中,然后运行ServletContextDemo2就可以从ServletContext对象中取出数据了,这样就实现了数据共享,如下图所示:

  img

  3.2、获取WEB应用的初始化参数

  在web.xml文件中使用标签配置WEB应用的初始化参数,如下所示:

1
2
3
4
<context-param>
<param-name>url</param-name>
<param-value>jdbc:mysql://localhost:3306/test</param-value>
</context-param>

获取参数代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
package org.wuqinghua.java.ch01;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* Created by wuqinghua on 17/6/30.
*/
@WebServlet("/servletContextDemo03")
public class ServletContextDemo03 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
//获取servletContext
ServletContext servletContext = this.getServletContext();
String url = servletContext.getInitParameter("url");
resp.getWriter().print(url);
}
}

运行结果:

  img

  3.3、用servletContext实现请求转发

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
package org.wuqinghua.java.ch01;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletContext;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* Created by wuqinghua on 17/6/30.
*/
@WebServlet(urlPatterns = {"/servletContextDemo04"})
public class ServletContextDemo04 extends HttpServlet {
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String data = "<h1><font color='red'>abcdefghijkl</font></h1>";
resp.getOutputStream().write(data.getBytes());
ServletContext servletContext = this.getServletContext();
RequestDispatcher rd = servletContext.getRequestDispatcher("/servletContextDemo05");
rd.forward(req,resp);
}
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
package org.wuqinghua.java.ch01;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* Created by wuqinghua on 17/6/30.
*/
@WebServlet(urlPatterns = "/servletContextDemo05")
public class ServletContextDemo05 extends HttpServlet{
@Override
protected void service(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.getOutputStream().write("servletContextDemo05".getBytes());
}
}

运行结果:

  img

  访问的是ServletContextDemo4,浏览器显示的却是ServletContextDemo5的内容,这就是使用ServletContext实现了请求转发

  3.4、利用ServletContext对象读取资源文件

项目目录结构如下:

  img

代码范例:使用servletContext读取资源文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
package gacl.servlet.study;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.IOException;
import java.io.InputStream;
import java.text.MessageFormat;
import java.util.Properties;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* 使用servletContext读取资源文件
*
* @author gacl
*
*/
public class ServletContextDemo6 extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
/**
* response.setContentType("text/html;charset=UTF-8");目的是控制浏览器用UTF-8进行解码;
* 这样就不会出现中文乱码了
*/
response.setHeader("content-type","text/html;charset=UTF-8");
readSrcDirPropCfgFile(response);//读取src目录下的properties配置文件
response.getWriter().println("<hr/>");
readWebRootDirPropCfgFile(response);//读取WebRoot目录下的properties配置文件
response.getWriter().println("<hr/>");
readPropCfgFile(response);//读取src目录下的db.config包中的db3.properties配置文件
response.getWriter().println("<hr/>");
readPropCfgFile2(response);//读取src目录下的gacl.servlet.study包中的db4.properties配置文件
}
/**
* 读取src目录下的gacl.servlet.study包中的db4.properties配置文件
* @param response
* @throws IOException
*/
private void readPropCfgFile2(HttpServletResponse response)
throws IOException {
InputStream in = this.getServletContext().getResourceAsStream("/WEB-INF/classes/gacl/servlet/study/db4.properties");
Properties prop = new Properties();
prop.load(in);
String driver = prop.getProperty("driver");
String url = prop.getProperty("url");
String username = prop.getProperty("username");
String password = prop.getProperty("password");
response.getWriter().println("读取src目录下的gacl.servlet.study包中的db4.properties配置文件:");
response.getWriter().println(
MessageFormat.format(
"driver={0},url={1},username={2},password={3}",
driver,url, username, password));
}
/**
* 读取src目录下的db.config包中的db3.properties配置文件
* @param response
* @throws FileNotFoundException
* @throws IOException
*/
private void readPropCfgFile(HttpServletResponse response)
throws FileNotFoundException, IOException {
//通过ServletContext获取web资源的绝对路径
String path = this.getServletContext().getRealPath("/WEB-INF/classes/db/config/db3.properties");
InputStream in = new FileInputStream(path);
Properties prop = new Properties();
prop.load(in);
String driver = prop.getProperty("driver");
String url = prop.getProperty("url");
String username = prop.getProperty("username");
String password = prop.getProperty("password");
response.getWriter().println("读取src目录下的db.config包中的db3.properties配置文件:");
response.getWriter().println(
MessageFormat.format(
"driver={0},url={1},username={2},password={3}",
driver,url, username, password));
}
/**
* 通过ServletContext对象读取WebRoot目录下的properties配置文件
* @param response
* @throws IOException
*/
private void readWebRootDirPropCfgFile(HttpServletResponse response)
throws IOException {
/**
* 通过ServletContext对象读取WebRoot目录下的properties配置文件
* “/”代表的是项目根目录
*/
InputStream in = this.getServletContext().getResourceAsStream("/db2.properties");
Properties prop = new Properties();
prop.load(in);
String driver = prop.getProperty("driver");
String url = prop.getProperty("url");
String username = prop.getProperty("username");
String password = prop.getProperty("password");
response.getWriter().println("读取WebRoot目录下的db2.properties配置文件:");
response.getWriter().print(
MessageFormat.format(
"driver={0},url={1},username={2},password={3}",
driver,url, username, password));
}
/**
* 通过ServletContext对象读取src目录下的properties配置文件
* @param response
* @throws IOException
*/
private void readSrcDirPropCfgFile(HttpServletResponse response) throws IOException {
/**
* 通过ServletContext对象读取src目录下的db1.properties配置文件
*/
InputStream in = this.getServletContext().getResourceAsStream("/WEB-INF/classes/db1.properties");
Properties prop = new Properties();
prop.load(in);
String driver = prop.getProperty("driver");
String url = prop.getProperty("url");
String username = prop.getProperty("username");
String password = prop.getProperty("password");
response.getWriter().println("读取src目录下的db1.properties配置文件:");
response.getWriter().println(
MessageFormat.format(
"driver={0},url={1},username={2},password={3}",
driver,url, username, password));
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
this.doGet(request, response);
}
}

运行结果如下:

  img

代码范例:使用类装载器读取资源文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
package gacl.servlet.study;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.text.MessageFormat;
import java.util.Properties;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/**
* 用类装载器读取资源文件
* 通过类装载器读取资源文件的注意事项:不适合装载大文件,否则会导致jvm内存溢出
* @author gacl
*
*/
public class ServletContextDemo7 extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
/**
* response.setContentType("text/html;charset=UTF-8");目的是控制浏览器用UTF-8进行解码;
* 这样就不会出现中文乱码了
*/
response.setHeader("content-type","text/html;charset=UTF-8");
test1(response);
response.getWriter().println("<hr/>");
test2(response);
response.getWriter().println("<hr/>");
//test3();
test4();
}
/**
* 读取类路径下的资源文件
* @param response
* @throws IOException
*/
private void test1(HttpServletResponse response) throws IOException {
//获取到装载当前类的类装载器
ClassLoader loader = ServletContextDemo7.class.getClassLoader();
//用类装载器读取src目录下的db1.properties配置文件
InputStream in = loader.getResourceAsStream("db1.properties");
Properties prop = new Properties();
prop.load(in);
String driver = prop.getProperty("driver");
String url = prop.getProperty("url");
String username = prop.getProperty("username");
String password = prop.getProperty("password");
response.getWriter().println("用类装载器读取src目录下的db1.properties配置文件:");
response.getWriter().println(
MessageFormat.format(
"driver={0},url={1},username={2},password={3}",
driver,url, username, password));
}
/**
* 读取类路径下面、包下面的资源文件
* @param response
* @throws IOException
*/
private void test2(HttpServletResponse response) throws IOException {
//获取到装载当前类的类装载器
ClassLoader loader = ServletContextDemo7.class.getClassLoader();
//用类装载器读取src目录下的gacl.servlet.study包中的db4.properties配置文件
InputStream in = loader.getResourceAsStream("gacl/servlet/study/db4.properties");
Properties prop = new Properties();
prop.load(in);
String driver = prop.getProperty("driver");
String url = prop.getProperty("url");
String username = prop.getProperty("username");
String password = prop.getProperty("password");
response.getWriter().println("用类装载器读取src目录下的gacl.servlet.study包中的db4.properties配置文件:");
response.getWriter().println(
MessageFormat.format(
"driver={0},url={1},username={2},password={3}",
driver,url, username, password));
}
/**
* 通过类装载器读取资源文件的注意事项:不适合装载大文件,否则会导致jvm内存溢出
*/
public void test3() {
/**
* 01.avi是一个150多M的文件,使用类加载器去读取这个大文件时会导致内存溢出:
* java.lang.OutOfMemoryError: Java heap space
*/
InputStream in = ServletContextDemo7.class.getClassLoader().getResourceAsStream("01.avi");
System.out.println(in);
}
/**
* 读取01.avi,并拷贝到e:\根目录下
* 01.avi文件太大,只能用servletContext去读取
* @throws IOException
*/
public void test4() throws IOException {
// path=G:\Java学习视频\JavaWeb学习视频\JavaWeb\day05视频\01.avi
// path=01.avi
String path = this.getServletContext().getRealPath("/WEB-INF/classes/01.avi");
/**
* path.lastIndexOf("\\") + 1是一个非常绝妙的写法
*/
String filename = path.substring(path.lastIndexOf("\\") + 1);//获取文件名
InputStream in = this.getServletContext().getResourceAsStream("/WEB-INF/classes/01.avi");
byte buffer[] = new byte[1024];
int len = 0;
OutputStream out = new FileOutputStream("e:\\" + filename);
while ((len = in.read(buffer)) > 0) {
out.write(buffer, 0, len);
}
out.close();
in.close();
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
this.doGet(request, response);
}
}

 运行结果如下:

  img

四、在客户端缓存Servlet的输出

  对于不经常变化的数据,在servlet中可以为其设置合理的缓存时间值,以避免浏览器频繁向服务器发送请求,提升服务器的性能。例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
package gacl.servlet.study;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class ServletDemo5 extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
String data = "abcddfwerwesfasfsadf";
/**
* 设置数据合理的缓存时间值,以避免浏览器频繁向服务器发送请求,提升服务器的性能
* 这里是将数据的缓存时间设置为1天
*/
response.setDateHeader("expires",System.currentTimeMillis() + 24 * 3600 * 1000);
response.getOutputStream().write(data.getBytes());
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
this.doGet(request, response);
}
}

JavaWeb学习总结(五)--Servlet开发(一)

发表于 2017-06-29 | 分类于 Java Web |

[TOC]

一、Servlet简介

  Servlet是sun公司提供的一门用于开发动态web资源的技术。
  Sun公司在其API中提供了一个servlet接口,用户若想用发一个动态web资源(即开发一个Java程序向浏览器输出数据),需要完成以下2个步骤:
  1、编写一个Java类,实现servlet接口。
  2、把开发好的Java类部署到web服务器中。
  按照一种约定俗成的称呼习惯,通常我们也把实现了servlet接口的java程序,称之为Servlet

二、Servlet的运行过程

Servlet程序是由WEB服务器调用,web服务器收到客户端的Servlet访问请求后:
  ①Web服务器首先检查是否已经装载并创建了该Servlet的实例对象。如果是,则直接执行第④步,否则,执行第②步。
  ②装载并创建该Servlet的一个实例对象。
  ③调用Servlet实例对象的init()方法。
  ④创建一个用于封装HTTP请求消息的HttpServletRequest对象和一个代表HTTP响应消息的HttpServletResponse对象,然后调用Servlet的service()方法并将请求和响应对象作为参数传递进去。
  ⑤WEB应用程序被停止或重新启动之前,Servlet引擎将卸载Servlet,并在卸载之前调用Servlet的destroy()方法。
img

img

img

img

img

img

img

img

三、Servlet调用图

 Servlet调用图

四、在Eclipse中开发Servlet

  在eclipse中新建一个web project工程,eclipse会自动创建下图所示目录结构:

 img

4.1、Servlet接口实现类

  Servlet接口SUN公司定义了两个默认实现类,分别为:GenericServlet、HttpServlet。

  HttpServlet指能够处理HTTP请求的servlet,它在原有Servlet接口上添加了一些与HTTP协议处理方法,它比Servlet接口的功能更为强大。因此开发人员在编写Servlet时,通常应继承这个类,而避免直接去实现Servlet接口。
  HttpServlet在实现Servlet接口时,覆写了service方法,该方法体内的代码会自动判断用户的请求方式,如为GET请求,则调用HttpServlet的doGet方法,如为Post请求,则调用doPost方法。因此,开发人员在编写Servlet时,通常只需要覆写doGet或doPost方法,而不要去覆写service方法。

4.2、通过Eclipse创建和编写Servlet

  选中gacl.servlet.study包,右键→New→Servlet,如下图所示:

  img

  img

  img

  这样,我们就通过Eclipse帮我们创建好一个名字为ServletDemo1的Servlet,创建好的ServletDemo01里面会有如下代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
package gacl.servlet.study;
import java.io.IOException;
import java.io.PrintWriter;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class ServletDemo1 extends HttpServlet {
/**
* The doGet method of the servlet. <br>
*
* This method is called when a form has its tag value method equals to get.
*
* @param request the request send by the client to the server
* @param response the response send by the server to the client
* @throws ServletException if an error occurred
* @throws IOException if an error occurred
*/
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html");
PrintWriter out = response.getWriter();
out.println("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">");
out.println("<HTML>");
out.println(" <HEAD><TITLE>A Servlet</TITLE></HEAD>");
out.println(" <BODY>");
out.print(" This is ");
out.print(this.getClass());
out.println(", using the GET method");
out.println(" </BODY>");
out.println("</HTML>");
out.flush();
out.close();
}
/**
* The doPost method of the servlet. <br>
*
* This method is called when a form has its tag value method equals to post.
*
* @param request the request send by the client to the server
* @param response the response send by the server to the client
* @throws ServletException if an error occurred
* @throws IOException if an error occurred
*/
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
response.setContentType("text/html");
PrintWriter out = response.getWriter();
out.println("<!DOCTYPE HTML PUBLIC \"-//W3C//DTD HTML 4.01 Transitional//EN\">");
out.println("<HTML>");
out.println(" <HEAD><TITLE>A Servlet</TITLE></HEAD>");
out.println(" <BODY>");
out.print(" This is ");
out.print(this.getClass());
out.println(", using the POST method");
out.println(" </BODY>");
out.println("</HTML>");
out.flush();
out.close();
}
}

  这些代码都是Eclipse自动生成的,而web.xml文件中也多了和两对标签,这两对标签是配置ServletDemo1的,如下图所示:

img

然后我们就可以通过浏览器访问ServletDemo1这个Servlet,如下图所示:

  img

五、Servlet开发注意细节

5.1、Servlet访问URL映射配置

  由于客户端是通过URL地址访问web服务器中的资源,所以Servlet程序若想被外界访问,必须把servlet程序映射到一个URL地址上,这个工作在web.xml文件中使用元素和元素完成。
  元素用于注册Servlet,它包含有两个主要的子元素:和,分别用于设置Servlet的注册名称和Servlet的完整类名。
一个元素用于映射一个已注册的Servlet的一个对外访问路径,它包含有两个子元素:和,分别用于指定Servlet的注册名称和Servlet的对外访问路径。例如:

1
2
3
4
5
6
7
8
9
<servlet>
<servlet-name>ServletDemo1</servlet-name>
<servlet-class>gacl.servlet.study.ServletDemo1</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>ServletDemo1</servlet-name>
<url-pattern>/servlet/ServletDemo1</url-pattern>
</servlet-mapping>

  同一个Servlet可以被映射到多个URL上,即多个元素的子元素的设置值可以是同一个Servlet的注册名。 例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
<servlet>
<servlet-name>ServletDemo1</servlet-name>
<servlet-class>gacl.servlet.study.ServletDemo1</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>ServletDemo1</servlet-name>
<url-pattern>/servlet/ServletDemo1</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>ServletDemo1</servlet-name>
<url-pattern>/1.htm</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>ServletDemo1</servlet-name>
<url-pattern>/2.jsp</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>ServletDemo1</servlet-name>
<url-pattern>/3.php</url-pattern>
</servlet-mapping>
<servlet-mapping>
<servlet-name>ServletDemo1</servlet-name>
<url-pattern>/4.ASPX</url-pattern>
</servlet-mapping>

  通过上面的配置,当我们想访问名称是ServletDemo1的Servlet,可以使用如下的几个地址去访问:

  http://localhost:8080/JavaWeb_Servlet_Study_20140531**/servlet/ServletDemo1**

  http://localhost:8080/JavaWeb_Servlet_Study_20140531**/1.htm**

  http://localhost:8080/JavaWeb_Servlet_Study_20140531**/2.jsp**

  http://localhost:8080/JavaWeb_Servlet_Study_20140531**/3.php**

  http://localhost:8080/JavaWeb_Servlet_Study_20140531**/4.ASPX**

  ServletDemo1被映射到了多个URL上。

5.2、Servlet访问URL使用*通配符映射  

在Servlet映射到的URL中也可以使用*通配符,但是只能有两种固定的格式:一种格式是”.扩展名”,另一种格式是以正斜杠(/)开头并以”/“结尾。例如:

  img

1
2
3
4
5
6
7
8
<servlet>
<servlet-name>ServletDemo1</servlet-name>
<servlet-class>gacl.servlet.study.ServletDemo1</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>ServletDemo1</servlet-name>
<url-pattern>/*</url-pattern>

  *可以匹配任意的字符,所以此时可以用任意的URL去访问ServletDemo1这个Servlet,如下图所示:

  img

对于如下的一些映射关系:
  Servlet1 映射到 /abc/
  Servlet2 映射到 /

  Servlet3 映射到 /abc
  Servlet4 映射到 .do
问题:
  当请求URL为“/abc/a.html”,“/abc/
”和“/”都匹配,哪个servlet响应
​   Servlet引擎将调用Servlet1。
  当请求URL为“/abc”时,“/abc/
”和“/abc”都匹配,哪个servlet响应
​   Servlet引擎将调用Servlet3。
  当请求URL为“/abc/a.do”时,“/abc/”和“.do”都匹配,哪个servlet响应
​   Servlet引擎将调用Servlet1。
  当请求URL为“/a.do”时,“/”和“.do”都匹配,哪个servlet响应
​   Servlet引擎将调用Servlet2。
  当请求URL为“/xxx/yyy/a.do”时,“/”和“.do”都匹配,哪个servlet响应
​   Servlet引擎将调用Servlet2。
  匹配的原则就是”谁长得更像就找谁”

5.3、Servlet与普通Java类的区别  

  Servlet是一个供其他Java程序(Servlet引擎)调用的Java类,它不能独立运行,它的运行完全由Servlet引擎来控制和调度。
  针对客户端的多次Servlet请求,通常情况下,服务器只会创建一个Servlet实例对象,也就是说Servlet实例对象一旦创建,它就会驻留在内存中,为后续的其它请求服务,直至web容器退出,servlet实例对象才会销毁。
  在Servlet的整个生命周期内,Servlet的init方法只被调用一次。而对一个Servlet的每次访问请求都导致Servlet引擎调用一次servlet的service方法。对于每次访问请求,Servlet引擎都会创建一个新的HttpServletRequest请求对象和一个新的HttpServletResponse响应对象,然后将这两个对象作为参数传递给它调用的Servlet的service()方法,service方法再根据请求方式分别调用doXXX方法。

  如果在元素中配置了一个元素,那么WEB应用程序在启动时,就会装载并创建Servlet的实例对象、以及调用Servlet实例对象的init()方法。
​ 举例:
​
​ invoker
​
​ org.apache.catalina.servlets.InvokerServlet
​

​ 1
​

  用途:为web应用写一个InitServlet,这个servlet配置为启动时装载,为整个web应用创建必要的数据库表和数据。

5.4、缺省Servlet

  如果某个Servlet的映射路径仅仅为一个正斜杠(/),那么这个Servlet就成为当前Web应用程序的缺省Servlet。
  凡是在web.xml文件中找不到匹配的元素的URL,它们的访问请求都将交给缺省Servlet处理,也就是说,缺省Servlet用于处理所有其他Servlet都不处理的访问请求。 例如:

1
2
3
4
5
6
7
8
9
10
11
<servlet>
<servlet-name>ServletDemo2</servlet-name>
<servlet-class>gacl.servlet.study.ServletDemo2</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<!-- 将ServletDemo2配置成缺省Servlet -->
<servlet-mapping>
<servlet-name>ServletDemo2</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>

  当访问不存在的Servlet时,就使用配置的默认Servlet进行处理,如下图所示:

  img

  在\conf\web.xml文件中,注册了一个名称为org.apache.catalina.servlets.DefaultServlet的Servlet,并将这个Servlet设置为了缺省Servlet。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
<servlet>
<servlet-name>default</servlet-name>
<servlet-class>org.apache.catalina.servlets.DefaultServlet</servlet-class>
<init-param>
<param-name>debug</param-name>
<param-value>0</param-value>
</init-param>
<init-param>
<param-name>listings</param-name>
<param-value>false</param-value>
</init-param>
<load-on-startup>1</load-on-startup>
</servlet>
<!-- The mapping for the default servlet -->
<servlet-mapping>
<servlet-name>default</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>

  当访问Tomcat服务器中的某个静态HTML文件和图片时,实际上是在访问这个缺省Servlet。

5.5、Servlet的线程安全问题

  当多个客户端并发访问同一个Servlet时,web服务器会为每一个客户端的访问请求创建一个线程,并在这个线程上调用Servlet的service方法,因此service方法内如果访问了同一个资源的话,就有可能引发线程安全问题。例如下面的代码:

不存在线程安全问题的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
package gacl.servlet.study;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class ServletDemo3 extends HttpServlet {
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
/**
* 当多线程并发访问这个方法里面的代码时,会存在线程安全问题吗
* i变量被多个线程并发访问,但是没有线程安全问题,因为i是doGet方法里面的局部变量,
* 当有多个线程并发访问doGet方法时,每一个线程里面都有自己的i变量,
* 各个线程操作的都是自己的i变量,所以不存在线程安全问题
* 多线程并发访问某一个方法的时候,如果在方法内部定义了一些资源(变量,集合等)
* 那么每一个线程都有这些东西,所以就不存在线程安全问题了
*/
int i=1;
i++;
response.getWriter().write(i);
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}

存在线程安全问题的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
package gacl.servlet.study;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class ServletDemo3 extends HttpServlet {
int i=1;
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
i++;
try {
Thread.sleep(1000*4);
} catch (InterruptedException e) {
e.printStackTrace();
}
response.getWriter().write(i+"");
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}

  把i定义成全局变量,当多个线程并发访问变量i时,就会存在线程安全问题了,如下图所示:同时开启两个浏览器模拟并发访问同一个Servlet,本来正常来说,第一个浏览器应该看到2,而第二个浏览器应该看到3的,结果两个浏览器都看到了3,这就不正常。

  img

  线程安全问题只存在多个线程并发操作同一个资源的情况下,所以在编写Servlet的时候,如果并发访问某一个资源(变量,集合等),就会存在线程安全问题,那么该如何解决这个问题呢?

先看看下面的代码:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
package gacl.servlet.study;
import java.io.IOException;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
public class ServletDemo3 extends HttpServlet {
int i=1;
public void doGet(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
/**
* 加了synchronized后,并发访问i时就不存在线程安全问题了,
* 为什么加了synchronized后就没有线程安全问题了呢?
* 假如现在有一个线程访问Servlet对象,那么它就先拿到了Servlet对象的那把锁
* 等到它执行完之后才会把锁还给Servlet对象,由于是它先拿到了Servlet对象的那把锁,
* 所以当有别的线程来访问这个Servlet对象时,由于锁已经被之前的线程拿走了,后面的线程只能排队等候了
*
*/
synchronized (this) {//在java中,每一个对象都有一把锁,这里的this指的就是Servlet对象
i++;
try {
Thread.sleep(1000*4);
} catch (InterruptedException e) {
e.printStackTrace();
}
response.getWriter().write(i+"");
}
}
public void doPost(HttpServletRequest request, HttpServletResponse response)
throws ServletException, IOException {
doGet(request, response);
}
}

  现在这种做法是给Servlet对象加了一把锁,保证任何时候都只有一个线程在访问该Servlet对象里面的资源,这样就不存在线程安全问题了,如下图所示:

  img

  这种做法虽然解决了线程安全问题,但是编写Servlet却万万不能用这种方式处理线程安全问题,假如有9999个人同时访问这个Servlet,那么这9999个人必须按先后顺序排队轮流访问。

  针对Servlet的线程安全问题,Sun公司是提供有解决方案的:让Servlet去实现一个SingleThreadModel接口,如果某个Servlet实现了SingleThreadModel接口,那么Servlet引擎将以单线程模式来调用其service方法。
  查看Sevlet的API可以看到,SingleThreadModel接口中没有定义任何方法和常量,在Java中,把没有定义任何方法和常量的接口称之为标记接口,经常看到的一个最典型的标记接口就是”Serializable“,这个接口也是没有定义任何方法和常量的,标记接口在Java中有什么用呢?主要作用就是给某个对象打上一个标志,告诉JVM,这个对象可以做什么,比如实现了”Serializable“接口的类的对象就可以被序列化,还有一个”Cloneable”接口,这个也是一个标记接口,在默认情况下,Java中的对象是不允许被克隆的,就像现实生活中的人一样,不允许克隆,但是只要实现了”Cloneable”接口,那么对象就可以被克隆了。

  让Servlet实现了SingleThreadModel接口,只要在Servlet类的定义中增加实现SingleThreadModel接口的声明即可。
  对于实现了SingleThreadModel接口的Servlet,Servlet引擎仍然支持对该Servlet的多线程并发访问,其采用的方式是产生多个Servlet实例对象,并发的每个线程分别调用一个独立的Servlet实例对象。
  实现SingleThreadModel接口并不能真正解决Servlet的线程安全问题,因为Servlet引擎会创建多个Servlet实例对象,而真正意义上解决多线程安全问题是指一个Servlet实例对象被多个线程同时调用的问题。事实上,在Servlet API 2.4中,已经将SingleThreadModel标记为Deprecated(过时的)。

JavaWeb学习总结(四)--Http协议

发表于 2017-06-28 | 分类于 Java Web |

[TOC]

一、什么是HTTP协议

  HTTP是hypertext transfer protocol(超文本传输协议)的简写,它是TCP/IP协议的一个应用层协议,用于定义WEB浏览器与WEB服务器之间交换数据的过程。客户端连上web服务器后,若想获得web服务器中的某个web资源,需遵守一定的通讯格式,HTTP协议用于定义客户端与web服务器通迅的格式。

二、HTTP协议的版本

  HTTP协议的版本:HTTP/1.0、HTTP/1.1

三、HTTP1.0和HTTP1.1的区别

  在HTTP1.0协议中,客户端与web服务器建立连接后,只能获得一个web资源。
  在HTTP1.1协议,允许客户端与web服务器建立连接后,在一个连接上获取多个web资源。

四、HTTP请求

4.1、HTTP请求包括的内容

  客户端连上服务器后,向服务器请求某个web资源,称之为客户端向服务器发送了一个HTTP请求。

一个完整的HTTP请求包括如下内容:一个请求行、若干消息头、以及实体内容
范例:

  img

4.2、HTTP请求的细节——请求行

  请求行中的GET称之为请求方式,请求方式有:POST、GET、HEAD、OPTIONS、DELETE、TRACE、PUT,常用的有: GET、 POST
  用户如果没有设置,默认情况下浏览器向服务器发送的都是get请求,例如在浏览器直接输地址访问,点超链接访问等都是get,用户如想把请求方式改为post,可通过更改表单的提交方式实现。
  不管POST或GET,都用于向服务器请求某个WEB资源,这两种方式的区别主要表现在数据传递上:如果请求方式为GET方式,则可以在请求的URL地址后以?的形式带上交给服务器的数据,多个数据之间以&进行分隔,例如:GET /mail/1.html?name=abc&password=xyz HTTP/1.1
  GET方式的特点:在URL地址后附带的参数是有限制的,其数据容量通常不能超过1K。
  如果请求方式为POST方式,则可以在请求的实体内容中向服务器发送数据,Post方式的特点:传送的数据量无限制。

4.3、HTTP请求的细节——消息头

  HTTP请求中的常用消息头

  accept:浏览器通过这个头告诉服务器,它所支持的数据类型
  Accept-Charset: 浏览器通过这个头告诉服务器,它支持哪种字符集
  Accept-Encoding:浏览器通过这个头告诉服务器,支持的压缩格式
  Accept-Language:浏览器通过这个头告诉服务器,它的语言环境
  Host:浏览器通过这个头告诉服务器,想访问哪台主机
  If-Modified-Since: 浏览器通过这个头告诉服务器,缓存数据的时间
  Referer:浏览器通过这个头告诉服务器,客户机是哪个页面来的 防盗链
  Connection:浏览器通过这个头告诉服务器,请求完后是断开链接还是何持链接

例如:

1
2
3
4
5
6
7
8
Accept: application/x-ms-application, image/jpeg, application/xaml+xml, image/gif, image/pjpeg,
application/x-ms-xbap, application/vnd.ms-excel, application/vnd.ms-powerpoint, application/msword, */*
Referer: http://localhost:8080/JavaWebDemoProject/Web/2.jsp
Accept-Language: zh-CN
User-Agent: Mozilla/4.0 (compatible; MSIE 8.0; Windows NT 6.1; WOW64; Trident/4.0; SLCC2; .NET CLR 2.0.50727; .NET CLR 3.5.30729; .NET CLR 3.0.30729; Media Center PC 6.0; .NET4.0C; .NET4.0E; InfoPath.3)
Accept-Encoding: gzip, deflate
Host: localhost:8080
Connection: Keep-Alive

五、HTTP响应

5.1、HTTP响应包括的内容

  一个HTTP响应代表服务器向客户端回送的数据,它包括: 一个状态行、若干消息头、以及实体内容 。

  img
范例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
HTTP/1.1 200 OK
Server: Apache-Coyote/1.1
Content-Type: text/html;charset=ISO-8859-1
Content-Length: 105
Date: Tue, 27 May 2014 16:23:28 GMT
<html>
<head>
<title>Hello World JSP</title>
</head>
<body>
Hello World!
</body>
</html>

5.2、HTTP响应的细节——状态行

  状态行格式: HTTP版本号 状态码 原因叙述
​ 举例:HTTP/1.1 200 OK
  状态码用于表示服务器对请求的处理结果,它是一个三位的十进制数。响应状态码分为5类,如下所示:
  img

5.3、HTTP响应细节——常用响应头

  HTTP响应中的常用响应头(消息头)
  Location: 服务器通过这个头,来告诉浏览器跳到哪里
  Server:服务器通过这个头,告诉浏览器服务器的型号
  Content-Encoding:服务器通过这个头,告诉浏览器,数据的压缩格式
  Content-Length: 服务器通过这个头,告诉浏览器回送数据的长度
  Content-Language: 服务器通过这个头,告诉浏览器语言环境
  Content-Type:服务器通过这个头,告诉浏览器回送数据的类型
  Refresh:服务器通过这个头,告诉浏览器定时刷新
  Content-Disposition: 服务器通过这个头,告诉浏览器以下载方式打数据
  Transfer-Encoding:服务器通过这个头,告诉浏览器数据是以分块方式回送的
  Expires: -1 控制浏览器不要缓存
  Cache-Control: no-cache
  Pragma: no-cache

六、在服务端设置响应头来控制客户端浏览器

###6.1、设置Location响应头,实现请求重定向

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
package org.wuqinghua.java.ch01;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* Created by wuqinghua on 17/6/27.
* 设置Location响应头,实现请求重定向
*/
public class ServletDemo01 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setStatus(302);//设置服务器的响应状态码
/**
*设置响应头,服务器通过Location这个头,来告诉浏览器跳到哪里,这就是所谓的请求的重定向
*/
resp.setHeader("Location","/JavaWeb_HttpProtocol_Study_20140528/1.jsp");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doGet(req, resp);
}
}

当在浏览器中使用URL地址”http://localhost:8080/JavaWeb_HttpProtocol_Study_20140528/servlet/ServletDemo01"访问ServletDemo01时,就可以看到服务器作出响应后发送到浏览器的状态码和响应头信息,如下图所示:

  img

  服务器返回一个302状态码告诉浏览器,你要的资源我没有,但是我通过Location响应头告诉你哪里有,而浏览器解析响应头Location后知道要跳转到/JavaWeb_HttpProtocol_Study_20140528/1.jsp页面,所以就会自动跳转到1.jsp,如下图所示:  img

6.2、设置Content-Encoding响应头,告诉浏览器数据的压缩格式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
package org.wuqinghua.java.ch01;
import com.sun.xml.internal.messaging.saaj.util.ByteOutputStream;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.util.zip.GZIPOutputStream;
/**
* Created by wuqinghua on 17/6/29.
* 这个程序主要两个知识点
* 1.使用GZIPOutputStream流来压缩数据
* 2.通过响应头Content-Encoding来告诉浏览器,服务器端发送的数据压缩格式
*/
public class ServletDemo02 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
String data = "abcdabcdabcdabcdabcdabcdab" +
"cdabcdabcdabcdabcdabcdabcdabcdabc" +
"dabcdabcdabcdabcdabcdabcdabcdabc" +
"dabcdabcdabcdabcdabcdabcdabcdabcdab" +
"cdabcdabcdabcdabcdabcdabcdabcdabcdab" +
"cdabcdabcdabcdabcdabcdabcdabcdabcdab" +
"cdabcdabcdabcdabcdabcdabcdabcdabcdab" +
"cdabcdabcdabcdabcdabcdabcdabcdabcdabcd";
System.out.println("原始数据大小:"+data.getBytes().length);
ByteOutputStream bout = new ByteOutputStream();
GZIPOutputStream gout = new GZIPOutputStream(bout);
gout.write(data.getBytes());
gout.close();
//获取压缩后的数据
byte[] g = bout.toByteArray();
resp.setHeader("Content-Encoding","gzip");
resp.setHeader("Content-Length",g.length+"");
resp.getOutputStream().write(g);
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doGet(req, resp);
}
}

服务器发给浏览器的响应信息如下:

imgimg

浏览器支持的压缩格式有:img

6.3、设置Content-type响应头,指定回送数据类型

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
package org.wuqinghua.java.ch01;
import javax.servlet.ServletException;
import javax.servlet.ServletOutputStream;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
/**
* Created by wuqinghua on 17/6/29.
*/
public class ServletDemo03 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
/**
* 浏览器能接收(Accept)的数据类型有:
* application/x-ms-application,
* image/jpeg,
* application/xaml+xml,
* image/gif,
* image/jpeg,
* application/x-ms-xbap,
* application/vnd.ms-excel,
* application/vnd.ms-powerpoint,
* application/msword,
*/
resp.setHeader("Content-type", "image/jpeg");//使用content-type响应头指定发送给浏览器的数据类型为image/jpeg
//读入图片
InputStream in = this.getServletContext().getResourceAsStream("/img/WP_20131005_002.jpg");
byte buffer[] = new byte[1024];
int len = -1;
OutputStream out = resp.getOutputStream();
while ((len = in.read(buffer, 0, buffer.length)) != -1) {
out.write(buffer, 0, len);
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doGet(req, resp);
}
}

服务器发给浏览器的响应信息如下:

img

ServletDemo03的运行结果如下图所示:

img

在浏览器中显示出了图片

6.4、设置refresh响应头,让浏览器定时刷新

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
package org.wuqinghua.java.ch01;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
/**
* Created by wuqinghua on 17/6/29.
*/
@WebServlet(name = "servletDemo04",urlPatterns = {"/servletDemo04"})
public class ServletDemo04 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
/**
* 设置refresh响应头,让浏览器每隔3秒定时刷新
*/
// resp.setHeader("refresh","3");
/**
* 设置refresh响应头,让浏览器3秒后跳转到http://www.baidu.com
*/
resp.setHeader("refresh", "3;url='http://www.baidu.com'");
resp.getWriter().write("gacl");
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doGet(req, resp);
}
}

6.5、设置content-disposition响应头,让浏览器下载

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
package org.wuqinghua.java.ch01;
import javax.servlet.ServletException;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
/**
* Created by wuqinghua on 17/6/29.
*/
@WebServlet(name = "servletDemo05", urlPatterns = {"/servletDemo05"})
public class ServletDemo05 extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
/**
* 设置content-disposition响应头,让浏览器下载文件
*/
resp.setHeader("content-disposition", "attachment;filename=xxx.jpg");
InputStream in = this.getServletContext().getResourceAsStream("/img/WP_20131005_002.jpg");
byte buffer[] = new byte[1024];
int len = -1;
OutputStream out = resp.getOutputStream();
while ((len = in.read(buffer, 0, buffer.length)) != -1) {
out.write(buffer, 0, len);
}
}
@Override
protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
super.doGet(req, resp);
}
}

在浏览器中访问ServletDemo05就会弹出文件下载框,如下图所示: img

12
Wu Qinghua

Wu Qinghua

纪录学习的点滴

18 日志
4 分类
4 标签
© 2017 Wu Qinghua
由 Hexo 强力驱动
主题 - NexT.Muse